﻿//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using MyBasicLib.Collections.Generic;

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using MyBasicLib.Collections.Generic;
namespace MyBasicLib.Data.ORMLiteNew
{
     public static class DbQueryableExtensions
    {
         public static System.Data.IDbCommand CreateCommand<TResult>(this IDbQueryable<TResult> source)
         {
             return (source.Provider as ORMLiteNew.DbQueryProvider).CreateCommand(DbQueryParser.Parse(source)); 
         }
         static DbQueryable<TResult> CreateQuery<TResult>(this IDbQueryable source)
         { 
             return new DbQueryable<TResult>() { Provider = source.Provider, DbExpressionList = new List<DbExpression>(source.DbExpressionList) };
         }
         static TResult Execute<TResult>(this IDbQueryable<TResult> source)  
         {
             return source.Provider.Execute<TResult>(source);
         }
         static IList<TResult> ExecuteList<TResult>(this IDbQueryable<TResult> source)  
         {
             return source.Provider.ExecuteList<TResult>(source);
         }
//        //public static bool All<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
//        //{
//        //    var newq = source.CreateQuery<bool>();
//        //    newq.DbExpressions.Add(new AllDbExpression(predicate));
//        //    var ret = newq.Execute();
//        //    return ret;
//        //}
//        //public static bool Any<TSource>(this IDbQueryable<TSource> source)
//        //{
//        //    return Any(source, null);
//        //}

//        //public static bool Any<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
//        //{
//        //    var newq = source.CreateQuery<bool>();
//        //    newq.DbExpressions.Add(new AnyDbExpression(predicate));
//        //    var ret = newq.Execute();
//        //    return ret;
//        //}
//        //public static double Average(this IDbQueryable<double> source);
//        //public static decimal? Average(this IDbQueryable<decimal?> source);
//        //public static double? Average(this IDbQueryable<double?> source);
//        //public static double? Average(this IDbQueryable<int?> source);
//        //public static decimal Average(this IDbQueryable<decimal> source);
//        //public static double? Average(this IDbQueryable<long?> source);
//        //public static double Average(this IDbQueryable<int> source);
//        //public static double Average(this IDbQueryable<long> source);
//        //public static float? Average(this IDbQueryable<float?> source);
//        //public static float Average(this IDbQueryable<float> source);
//        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int?>> selector);
//        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long?>> selector);
//        //public static decimal Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal>> selector);
//        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double?>> selector);
//        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double>> selector);
//        //public static decimal? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector);
//        //public static float? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float?>> selector);
//        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int>> selector);
//        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long>> selector);
//        //public static float Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float>> selector);

//        //public static IDbQueryable<TResult> Cast<TResult>(this IDbQueryable source);

         public static int Count<TSource>(this IDbQueryable<TSource> source)
         {
             return Count(source, null);
         }

         public static int Count<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
         { 
             IDbQueryable<int> newq = source.CreateQuery<int>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.Count,predicate));
             int ret = newq.Execute();
             return ret;
         }

         public static IDbQueryable<TSource> DefaultIfEmpty<TSource>(this IDbQueryable<TSource> source)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.DefaultIfEmpty));
             return source;
         }

//        //public static IDbQueryable<TSource> DefaultIfEmpty<TSource>(this IDbQueryable<TSource> source, TSource defaultValue);
         public static IDbQueryable<TSource> Distinct<TSource>(this IDbQueryable<TSource> source)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.Distinct)); 
             return source;
         }

//        //public static IDbQueryable<TSource> Except<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2);

         public static TSource First<TSource>(this IDbQueryable<TSource> source)
         {
             return source.First(null);
         }

         public static TSource First<TSource>(this IDbQueryable<TSource> source,
                                              Expression<Func<TSource, bool>> predicate)
         {
             IDbQueryable<TSource> newq = source.CreateQuery<TSource>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.First, predicate)); 
             TSource ret = newq.Execute();
             return ret;
         }

//        //public static TSource FirstOrDefault<TSource>(this IDbQueryable<TSource> source);
//        //public static TSource FirstOrDefault<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

//        //private static Expression GetSourceExpression<TSource>(IEnumerable<TSource> source);

//        //todo 
//        //public static IDbQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
//        //public static IDbQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey> comparer);
//        //public static IDbQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector);
//        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector);
//        //public static IDbQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, IEqualityComparer<TKey> comparer);
//        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector);
//        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector, IEqualityComparer<TKey> comparer); 
         public static IDbQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IDbQueryable<TOuter> outer,
                                                                                      IDbQueryable<TInner> inner,
                                                                                      Expression<Func<TOuter, TKey>>
                                                                                          outerKeySelector,
                                                                                      Expression<Func<TInner, TKey>>
                                                                                          innerKeySelector,
                                                                                      Expression
                                                                                          <
                                                                                          Func
                                                                                          <TOuter, IDbQueryable<TInner>,
                                                                                          TResult>> resultSelector)
         {
             IDbQueryable<TResult> newq = outer.CreateQuery<TResult>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.GroupJoin)
             {
                 Expressions = new Expression[]{
                   Expression.Constant( inner),
                   outerKeySelector,
                   innerKeySelector,
                   resultSelector}
             });
             return newq;
         }

//        //public static IDbQueryable<TSource> Intersect<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2);
//        //public static IDbQueryable<TSource> Intersect<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2, IEqualityComparer<TSource> comparer);

//        //todo
         public static IDbQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this IDbQueryable<TOuter> outer,
                                                                                 IDbQueryable<TInner> inner,
                                                                                 Expression<Func<TOuter, TKey>>
                                                                                     outerKeySelector,
                                                                                 Expression<Func<TInner, TKey>>
                                                                                     innerKeySelector,
                                                                                 Expression
                                                                                     <Func<TOuter, TInner, TResult>>
                                                                                     resultSelector)
         {
             IDbQueryable<TResult> newq = outer.CreateQuery<TResult>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.Join)
             {
                 Expressions = new Expression[]{
                   Expression.Constant( inner),
                   outerKeySelector,
                   innerKeySelector,
                   resultSelector}
             });
             return newq;
         }


//        //public static long LongCount<TSource>(this IDbQueryable<TSource> source);
//        //public static long LongCount<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

//        //public static TSource Max<TSource>(this IDbQueryable<TSource> source);
//        //public static TResult Max<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TResult>> selector);
//        //public static TSource Min<TSource>(this IDbQueryable<TSource> source);
//        //public static TResult Min<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TResult>> selector);

         public static IDbQueryable<TSource> OrderBy<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                    Expression<Func<TSource, TKey>> keySelector)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.OrderBy,keySelector));
             return source;
         }

         public static IDbQueryable<TSource> OrderBy<TSource>(this IDbQueryable<TSource> source,
                                                              string key)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.OrderBy, Expression.Constant(key))); 
             return source;
         }

         public static IDbQueryable<TSource> OrderByDescending<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                              Expression<Func<TSource, TKey>> keySelector)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.OrderByDescending, keySelector)); 
             return source;
         }

         public static IDbQueryable<TSource> OrderByDescending<TSource>(
             this IDbQueryable<TSource> source, string key)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.OrderByDescending, Expression.Constant(key))); 
             return source;
         }

//        //public static IDbQueryable<TSource> Reverse<TSource>(this IDbQueryable<TSource> source);

         public static IDbQueryable<TResult> Select<TSource, TResult>(this IDbQueryable<TSource> source,
                                                                      Expression<Func<TSource, TResult>> selector)
         {
             IDbQueryable<TResult> newq = source.CreateQuery<TResult>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.Select ,selector));

             return newq;
         }

//        //todo
//        //public static IDbQueryable<TResult> SelectMany<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, IEnumerable<TResult>>> selector)
//        //{
//        //    var newq = source.CreateQuery<TResult>();
//        //    newq.DbExpressions.Add(new SelectManyDbExpression()
//        //    {

//        //    });
//        //    return newq;

//        //}
         //public static IDbQueryable<TResult> SelectMany<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, int, IEnumerable<TResult>>> selector);
         public static IDbQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDbQueryable<TSource> source,
                                                                                       Expression
                                                                                           <
                                                                                           Func
                                                                                           <TSource,
                                                                                           IDbQueryable<TCollection>>>
                                                                                           collectionSelector,
                                                                                       Expression
                                                                                           <
                                                                                           Func
                                                                                           <TSource, TCollection, TResult
                                                                                           >> resultSelector)
         {

             IDbQueryable<TResult> newq = source.CreateQuery<TResult>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.SelectMany)
             {
                 Expressions = new Expression[]{ 
                   collectionSelector, 
                   resultSelector}
             });
             return newq;
         }

//        //public static IDbQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, int, IEnumerable<TCollection>>> collectionSelector, Expression<Func<TSource, TCollection, TResult>> resultSelector);


         public static TSource Single<TSource>(this IDbQueryable<TSource> source)
         {
             return source.Single(null);
         }

         public static TSource Single<TSource>(this IDbQueryable<TSource> source,
                                               Expression<Func<TSource, bool>> predicate)
         {
             IDbQueryable<TSource> newq = source.CreateQuery<TSource>();
             newq.DbExpressionList.Add(new DbExpression(DbExpressionType.Single,predicate));
             TSource ret = newq.Execute();
             return ret;
         }

//        //public static TSource SingleOrDefault<TSource>(this IDbQueryable<TSource> source);
//        //public static TSource SingleOrDefault<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

         public static IDbQueryable<TSource> Skip<TSource>(this IDbQueryable<TSource> source, int count)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.Skip,Expression.Constant (count)));

             return source;
         }

//        //public static decimal Sum(this IDbQueryable<decimal> source);
//        //public static double Sum(this IDbQueryable<double> source);
//        //public static int Sum(this IDbQueryable<int> source);
//        //public static int? Sum(this IDbQueryable<int?> source);
//        //public static long Sum(this IDbQueryable<long> source);
//        //public static decimal? Sum(this IDbQueryable<decimal?> source);
//        //public static double? Sum(this IDbQueryable<double?> source);
//        //public static long? Sum(this IDbQueryable<long?> source);
//        //public static float? Sum(this IDbQueryable<float?> source);
//        //public static float Sum(this IDbQueryable<float> source);
//        //public static double Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double>> selector);
//        //public static decimal? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector);
//        //public static double? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double?>> selector);
//        //public static int? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int?>> selector);
//        //public static long? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long?>> selector);
//        //public static float? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float?>> selector);
//        //public static decimal Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal>> selector);
//        //public static int Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int>> selector);
//        //public static long Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long>> selector);
//        //public static float Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float>> selector);

         public static IDbQueryable<TSource> Take<TSource>(this IDbQueryable<TSource> source, int count)
         { 
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.Take, Expression.Constant(count)));

             return source;
         }

         public static IDbQueryable<TSource> ThenBy<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                   Expression<Func<TSource, TKey>> keySelector)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.ThenBy, keySelector)); 
             return source;
         }

         public static IDbQueryable<TSource> ThenByDescending<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                             Expression<Func<TSource, TKey>> keySelector)
         {
             source.DbExpressionList.Add(new DbExpression(DbExpressionType.ThenByDescending, keySelector)); 
             return source;
         }

         //public static IDbQueryable<TSource> Union<TSource>(this IDbQueryable<TSource> source1, IDbQueryable<TSource> source2)
         //{
         //    source1.DbExpressionList.Add(new DbExpression(DbExpressionType.Union, source2));
         //    return source1;
         //}
//        //public static IDbQueryable<TSource> Union<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2, IEqualityComparer<TSource> comparer);

         public static IDbQueryable<TSource> Where<TSource>(this IDbQueryable<TSource> source,
                                                            Expression<Func<TSource, bool>> predicate)
         { 

             source.DbExpressionList.Add(new DbExpression(DbExpressionType.Where, predicate)); 

             return source;
         }

//        //public static IDbQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IDbQueryable<TFirst> source1, IEnumerable<TSecond> source2, Expression<Func<TFirst, TSecond, TResult>> resultSelector);


         public static TSource[] ToArray<TSource>(this IDbQueryable<TSource> source)
         {
             return ToList(source).ToArray();
         }

         public static TElement[] ToArray<TElement>(this IDbQueryable<TElement> source, int index,
                                                    int pageSize)
         {
             if (index < 1)
                 index = 1;
             return source.Skip((index - 1) * pageSize).Take(pageSize).ToArray();
         }

        public static List<TElement> ToList<TElement>(this IDbQueryable<TElement> source)
        {
            IEnumerable<TElement> data = source.Provider.ExecuteList<TElement>(source);
            if (data == null) return new List<TElement>(0);
            return new List<TElement>(data);
        }

        public static List<TElement> ToList<TElement>(this IDbQueryable<TElement> source, int index,
                                                      int pageSize)
        {
            if (index < 1)
                index = 1;
            return source.Skip((index - 1) * pageSize).Take(pageSize).ToList();
        }

        public static PagedList<TElement> ToPagedList<TElement>(this IDbQueryable<TElement> source, int index,
                                                                int pageSize)
        {
            if (pageSize == 0) 
                pageSize = 10;
            int count = source.Count();
            int pages = count / pageSize;
            if (count % pageSize > 0) pages++;
            if (index > pages)
            {
                index = pages;
            }
            if (index < 1)
                index = 1;
            List<TElement> ds = source.ToList(index, pageSize);
            return new PagedList<TElement>(ds, index, pageSize, count);
        }
    }
 }
